﻿using Abp.AspNetCore;
using Abp.AspNetCore.SignalR.Hubs;
using Abp.Castle.Logging.Log4Net;
using Castle.Facilities.Logging;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
using NetCore.Authentication.JwtBearer;
using NetCore.Configuration;
using NetCore.Identity;
using NetCore.Locks;
using NetCore.ServiceGroup.Esign;
using NetCore.Web.Resources;
using System;
using NetCore.SignalR;


namespace NetCore.Web.Startup
{
    public class Startup
    {
        private readonly IConfigurationRoot _appConfiguration;
        /// <summary>
        /// 构造函数注入env,根据不同环境获取Configration读写配置文件
        /// </summary>
        /// <param name="env"></param>
        public Startup(IHostingEnvironment env)
        {
            _appConfiguration = env.GetAppConfiguration();
        }
        /// <summary>
        /// 被运行时调用(mvc里面静态构造函数就是被运行时调用的)
        /// 向容器注册一些默认的服务(服务配置中心)
        /// Asp.Net 不负责请求的监听-转发-响应依靠(IIS)封装了管道处理模型，只写业务处理逻辑
        /// Asp.NetCore 请求-监听-转发-响应都是自己完成的，包括管道模型可以自定义
        /// 这里不再像以前那样，什么都封装好了开发者什么都不知道，而是公开了可扩展，可变更
        ///（类似nodejs一样，一个进程包含请求-监听-转发-响应）封装少，功能少，速度快
        /// </summary>
        /// <param name="services">IServiceCollection就是一个容器</param>
        /// <returns></returns>
        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            //services.AddSession();//1、添加session
            //使用CookiePolicyOptions会导致session失败 为什么不能同时使用？
            //services.Configure<CookiePolicyOptions>(options =>
            //{
            //    // This lambda determines whether user consent for non-essential cookies is needed for a given request.
            //    options.CheckConsentNeeded = context => true;
            //    options.MinimumSameSitePolicy = SameSiteMode.None;
            //});

            //services.AddResponseCompression();

            // MVC
            services.AddMvc(
                //o.Filters.Add<CustomExceptionFilterAttribute>();//异常处理全局filter
                //o.Filters.Add(typeof(CustomGlobalActionFilterAttribute));//全局注册filter
                options => options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute())
            );

            IdentityRegistrar.Register(services);
            AuthConfigurer.Configure(services, _appConfiguration);

            services.AddScoped<IWebResourceManager, WebResourceManager>();
            services.AddLock();
            services.AddSignalR();

            #region 微服务及节点配置

            services.RegisterHttpsApi(_appConfiguration);
            //将指定节点Json格式数据转为一个新的对象
            services.ConfigureOption(_appConfiguration.GetSection("App:MicroServices:CertificateServer:Option"), () => new ESignOption());

            //services.ConfigureOption(appConfiguration.GetSection("ClientToken"), () => new ClientTokenOption());
            //services.ConfigureOption(appConfiguration.GetSection("CloudConfigOptions"), () => new CloudConfigOptions());
            //services.ConfigureOption(appConfiguration.GetSection("App:MicroServices:SignServer:Option"), () => new SignOption());
            //services.ConfigureOption(appConfiguration.GetSection("App:MicroServices:CertificateServer:Option"), () => new ESignOption());
            #endregion

            //services.AddScoped<CustomActionFilterAttribute>();//告诉DI ServiceFilter
            //services.AddLock();//添加用户锁定
            //services.AddHttpsRedirection(opt => opt.HttpsPort = 443);//代码强制指定ssl端口443

            #region 设置自己的schema的handler
            //services.AddAuthenticationCore(options => options.AddScheme<MyHandler>("eScheme", "demo scheme"));
            ////权限认证需要的Policy AdvancedOnly
            //services.AddAuthorization(options =>
            //{
            //    options.AddPolicy("AdvancedOnly",
            //        authBuilder =>
            //        {
            //            authBuilder.RequireRole("Administrators");
            //        });
            //});
            #endregion

            ////codefirst代码优先:ef 连接字符串获取及设置DbContext  连接字符串在NetCore.Web.Core的module里面已设置
            //var connection = Configuration.GetConnectionString("netcore");//Configuration["ConnectionStrings:default"]
            //services.AddDbContext<NetCoreDbContext>(options => options.UseSqlServer(connection));


            // abp入口：配置abp框架以及依赖注入服务（添加log4net） Configure Abp and Dependency Injection
            //这里返回类型变成了 IServiceProvider ，这是因为在 AddAbp 方法内部替换了 Ioc 容器，变成了 CastleWindsor
            return services.AddAbp<NetCoreWebMvcModule>(
                // Configure Log4Net logging
                options => options.IocManager.IocContainer.AddFacility<LoggingFacility>(
                    f => f.UseAbpLog4Net().WithConfig("log4net.config")
                )
            );
        }

        /// <summary>
        /// 被运行时调用，主要用来设置http请求管道，(可以自定义添加一些接口参数依赖注入进来)
        /// 对比framework环境下管道处理模型：19个事件，是一个全家桶，把所有需要的东西全部封装在内，如果需要扩展就注册不同的事件进行扩展，但是执行顺序是不变的。
        /// core环境下的管道处理模型：把每一个执行块自由组装起来，自行调整顺序，最终形成一个请求数据流向，每个节点链接起来像一条链子/管道一样...
        /// </summary>
        /// <param name="app">IApplicationBuilder 对此接口的实例(中间件)做扩展</param>
        /// <param name="env"></param>
        /// <param name="loggerFactory"></param>
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            #region app.Run  app.Use  app.UseWhen

            #region 中间件解读 app.Use方法的参数(中间件)分解:
            ////管道：对http请求context的一连串的处理过程
            ////use中间件-接收一个RequestDelegate参数--返回RequestDelegate结果  IApplicationBuilder(app)应用程序的组装者
            ////IApplicationBuilder 在build之后最终就是一个RequestDelegate，能对httpcontext进行处理
            ////(build)之后所有的委托顺序集合都会倒序，并且每次循环后requestDelegate = func(requestDelegate);表示为委托a(n)=Funca(n-1)
            ////默认情况下，管道是空的，就是404，可以根据你的诉求，任意配置你的请求
            ////RequestDelegate参数倒叙之后 a4  a3 a2 a1
            ////requestDelegate = func(requestDelegate);之后初始requestDelegate 为返回404的一个delegate 假设为a5
            ////根据以上公式得: a5=func(a4)==>a4=a5.invoke(context)
            ////同理下一次循环：a4=func(a3)==>a3=a4.invoke(context)
            ////同理下一次循环：a3=func(a2)==>a2=a3.invoke(context)
            ////同理下一次循环：a2=func(a1)==>a1=a2.invoke(context)  综上：下一个委托等于上一个委托的前半部分内容，
            ////简单来说就是把一个context丢进一个中间件里面，处理一下，处理完了在把这个context返回回来然后丢给下一个中间件处理，
            ////上一个中间件的结果可以做为下一个中间件的参数 以此类推，可以组成一个请求处理的管道了
            ////一直处理请求到没有下一个中间件为止，如果最后一个中间end件没有指向下一个要执行的中间件了，
            ////那么就会执行当前end中间件后面代码部分内容，然后往上一个中间件中返回，返回后再执行上一个中间件后面的内容
            ////利用await async的特性，一次一次递归回去，就形成了中间件前后的逻辑像一个俄罗斯套娃一样执行
            ////（a1Start-->a2Start-->a3Start-->a4Start-->404(a5End)-->a4End-->a3End-->a2End-->a1End-->跳出中间件）。。。
            //Func<RequestDelegate, RequestDelegate> middleware = a1 =>
            //{
            //    Console.WriteLine("this is middleware");
            //    RequestDelegate a2 = new RequestDelegate(async context =>
            //      {
            //          await context.Response.WriteAsync("<h3>this is middleware start.....<h3>");
            //          await a1.Invoke(context);//a2=func(a1)==>a1=a2.invoke(context)
            //                                   //a1.Invoke(context)
            //                                   //=a2.Invoke(context).Invoke(context)
            //                                   //=a3.Invoke(context).Invoke(context).Invoke(context)//a3正好为middleware2的第一个参数,就这样循环就串起来一个请求管道了
            //                                   //=a4.Invoke(context).Invoke(context).Invoke(context).Invoke(context)
            //                                   //=a5.Invoke(context).Invoke(context).Invoke(context).Invoke(context).Invoke(context) //所以从a5(404)最开始执行
            //        await context.Response.WriteAsync("<h3>this is middleware end.......<h3>");
            //      });
            //    return a2;
            //};//{a3=fun(a2)==>a2=a3.Invoke(context)} 所以a1委托前半截执行完毕后马上await a2.invoke(context).invoke(context)
            ////await a3.invoke(context).invoke(context).invoke(context)->a2再交给a3而a3恰恰为组装者(app)进行下一个管道的第一个参数就这样循环
            //app.Use(middleware);
            //Func<RequestDelegate, RequestDelegate> middleware2 = a3 =>
            //{
            //    Console.WriteLine("this is middleware2");
            //    RequestDelegate a4 = new RequestDelegate(async context =>
            //      {
            //          await context.Response.WriteAsync("<h3>this is middleware2 start....<h3>");
            //        //await a3.Invoke(context);
            //        await context.Response.WriteAsync("<h3>this is middleware2 end......<h3>");
            //      });
            //    return a4;
            //};
            //app.Use(middleware2);

            ////IApplicationBuilder 在build之后最终就是一个RequestDelegate，能对httpcontext进行处理
            ////默认情况下，管道是空的，就是404，可以根据你的诉求，任意配置你的请求
            ////(build)之后所有的委托顺序集合都会倒序，并且每次循环后requestDelegate = func(requestDelegate);表示为委托a(n)=Funca(n-1)
            ////RequestDelegate参数倒叙之后 a4  a3 a2 a1   
            ////requestDelegate = func(requestDelegate);之后初始requestDelegate 为返回404的一个delegate 假设为a5
            ////根据以上公式得: a5=func(a4)==>a4=a5.invoke(context)
            ////同理下一次循环：a4=func(a3)==>a3=a4.invoke(context)
            ////同理下一次循环：a3=func(a2)==>a2=a3.invoke(context)
            ////同理下一次循环：a2=func(a1)==>a2=a1.invoke(context)

            ////简单来说就是把一个context丢进一个中间件里面，处理一下，处理完了在把这个context返回回来然后丢给下一个中间件处理，
            ////上一个中间件的结果可以做为下一个中间件的参数 以此类推，可以组成一个请求处理的管道了
            ////一直处理请求到没有下一个中间件为止，如果最后一个中间end件没有指向下一个要执行的中间件了，
            ////那么就会执行当前end中间件后面代码部分内容，然后往上一个中间件中返回，返回后再执行上一个中间件后面的内容
            ////利用await async的特性，一次一次递归回去，就形成了中间件前后的逻辑像一个俄罗斯套娃一样执行
            ////（a1Start-->a2Start-->a3Start-->a4Start-->404(End)-->a4End-->a3End-->a2End-->a1End-->跳出中间件）。。。
            ////这个管道的任何一个环节，或者任何一个中间件里面都可以添加自己的扩展，哇，完美！
            ////不管你在哪里都可以学习到的是全世界的智慧，还有Github上麻省理工各种开源(devExtreme)，上帝的创造真伟大。

            ////1、Run 终结式的 只是去执行 没有执行Next 所以它之后所有中间件都不会执行啦
            ////它可以作为终结点
            ////app.Run(c => c.Response.WriteAsync("被我替换了，以下代码不再执行"));

            #endregion


            //app.UseWhen(context =>
            //{
            //    return context.Request.QueryString.Value.Contains("Chensq");

            //}, appBulder => {
            //    appBulder.Use(async (context, next) =>
            //    {
            //        await context.Response.WriteAsync("<h3>this is Middleware UseWhen start<h3>");
            //        await next.Invoke();
            //        await context.Response.WriteAsync("<h3>this is Middleware UseWhen end<h3>");

            //    });
            //});

            //app.Use(next =>
            //{
            //    Console.WriteLine("this is Middleware3");
            //    return new RequestDelegate(async context =>
            //    {
            //        //var htmlString = new HtmlString("<h3>this is Middleware3 start<h3>");
            //        await context.Response.WriteAsync("<h3>this is Middleware3 start<h3>");
            //        //await next.Invoke(context);// 如果没有调用next.Invoke(context) 那就是终结点
            //        await context.Response.WriteAsync("<h3>this is Middleware3 end<h3>");
            //    });
            //});

            //最好不要在中间件里面判断选择分支，可以多写几个中间件，一个中间件只做一件事情


            #endregion

            #region app.Map app.MapWhen 

            ////Map:根据条件指定中间件
            //app.Map("/Test", MapTest);//路由中有Test 就交给MapTest处理
            ////满足条件 就直接给MapTest 处理并且不再往后执行
            ////可以做 拒绝非Chrome请求、多语言、Ajax统一处理等
            ////
            //app.MapWhen(context => context.Request.Query.ContainsKey("name"), MapTest);


            #endregion

            #region 认证授权基本模型
            //// 登录
            //app.Map("/login", builder => builder.Use(next =>
            //{
            //    return async (context) =>
            //    {
            //        var claimIdentity = new ClaimsIdentity();
            //        claimIdentity.AddClaim(new Claim(ClaimTypes.Name, "Madling"));
            //        await context.SignInAsync("eScheme", new ClaimsPrincipal(claimIdentity));
            //        await context.Response.WriteAsync($"Hello, ASP.NET Core! {context.User.Identity.Name} Login");
            //    };
            //}));
            //// 退出
            //app.Map("/logout", builder => builder.Use(next =>
            //{
            //    return async (context) =>
            //    {
            //        await context.SignOutAsync("eScheme");
            //    };
            //}));

            //app.Use(next => //认证   认识身份证
            //{
            //    return async (context) =>
            //    {
            //        var result = await context.AuthenticateAsync("eScheme");
            //        if (result?.Principal != null)
            //        {
            //            context.User = result.Principal;
            //            await next(context);
            //        }
            //        else
            //        {
            //            await context.ChallengeAsync("eScheme");
            //        }
            //    };
            //});
            //// 授权 身份证有什么权限
            //app.Use(async (context, next) =>
            //{
            //    var user = context.User;
            //    if (user.Identity.Name.Equals("Madling"))//user?.Identity?.IsAuthenticated这里没有授权检测 只检查下名称
            //    {
            //        await next();
            //    }
            //    else
            //    {
            //        await context.ForbidAsync("eScheme");
            //    }
            //    //if (user?.Identity?.IsAuthenticated ?? false)
            //    //{
            //    //    if (user.Identity.Name != "jim") await context.ForbidAsync("eScheme");
            //    //    else await next();
            //    //}
            //    //else
            //    //{
            //    //    await context.ChallengeAsync("eScheme");
            //    //}
            //});

            //// 上面权限认证通过以后，俄罗斯套娃模式，才可以防伪码后面的中间件：访问受保护资源
            //app.Map("/resource", builder => builder.Run(async (context) =>
            //{
            //    await context.Response.WriteAsync($"Hello, ASP.NET Core! {context.User.Identity.Name}");
            //}));

            //app.Run(async (HttpContext context) =>
            //{
            //    await context.Response.WriteAsync("Hello World,success！");
            //});
            #endregion

            #region 自定义中间件
            //Middleware 类 自定义中间件 自定义中间件执行有误
            //app.UseMiddleware<BowenMiddleware>();
            #endregion
            //这里都可以记录日志,在参数中注入ILoggerFactory factory
            //随便传递一个类型都可以，创建日志模板
            ILogger<Startup> logger = loggerFactory.CreateLogger<Startup>();
            logger.LogDebug($"{nameof(Startup)}项目启动");

            app.UseAbp(); //配置abp框架 Initializes ABP framework.

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
            }

            //app.UseStaticFiles();
            //处理静态文件 程序目录下wwwroot目录的静态文件内容 httpHandler-staticfileHandler 引用的样式文件等....
            app.UseStaticFiles(new StaticFileOptions
            {
                OnPrepareResponse = ctx =>
                {
                    const int durationInSeconds = 60 * 60 * 24;//缓存24h
                    ctx.Context.Response.Headers[HeaderNames.CacheControl] =
                        "public,max-age=" + durationInSeconds;
                }
            });//asp-append-version="true"  为静态文件提供版本号

            //app.UseCookiePolicy();//识别cookie

            app.UseAuthentication();//支持授权

            app.UseJwtTokenMiddleware();

            //SignalR
            app.UseSignalR(routes =>
            {
                routes.MapHub<NotifyHub>(_appConfiguration["Abp:SignalR"]);//读配置文件
                routes.MapHub<AbpCommonHub>("/signalr");
            });

            #region 配置文件的读取 网站目录\appsettings.json
            //xml path
            //Console.WriteLine($"work = {this.Configuration["work"]}");
            //Console.WriteLine($"work1 = {this.Configuration["work:work1"]}");
            //Console.WriteLine($"option1 = {this.Configuration["Option1"]}");
            //Console.WriteLine($"option2 = {this.Configuration["option2"]}");
            //Console.WriteLine($"suboption1 = {this.Configuration["subsection:suboption1"]}");
            //Console.WriteLine("Wizards:");
            //Console.Write($"{this.Configuration["wizards:0:Name"]}, ");
            //Console.WriteLine($"age {this.Configuration["wizards:0:Age"]}");
            //Console.Write($"{this.Configuration["wizards:1:Name"]}, ");
            //Console.WriteLine($"age {this.Configuration["wizards:1:Age"]}");
            #endregion

            app.UseMvc(routes =>
            {
                //区域的默认配置
                //routes.MapRoute(
                //    name : "areas",
                //    template : "{area:exists}/{controller=Home}/{action=Index}/{id?}"
                //);
                routes.MapRoute(
                    name: "defaultWithArea",
                    template: "{area}/{controller=Home}/{action=Index}/{id?}");

                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");//默认路由

                //特性路由---webapi的路由是一样的

                //routes.MapRoute(
                //    name: "old_default",
                //    template: "{controller}/{action}/{id}",
                //    defaults: new { controller = "Home", action = "Index", Id = -1 }
                //    );
            });

            //程序停止调用函数
            //appLifetime.ApplicationStopped.Register(() => { AutofacContainer.Dispose(); });
        }

        /// <summary>
        /// MapTest 自定义
        /// </summary>
        /// <param name="app"></param>
        public void MapTest(IApplicationBuilder app)
        {
            app.Run(async context =>
            {
                await context.Response.WriteAsync($"Url is {context.Request.Path.Value}");
            });
        }
    }
}
